#include "display.h"
#include "flash.h"
#include "iap.h"
#include "usb.h"
#include "version.h"

#include <string.h>
#include <usb_lib.h>
#include <usb_desc.h>
#include <usb_istr.h>
#include <usb_pwr.h>

#define	ROM_BASE	0x08000000

static void* bootVect=(void*)0x08000000;

static ubyte checkSum(ushort *rx,ushort ord){
	ubyte *p=(ubyte*)version_bottom;
	ubyte *e=(ubyte*)(ROM_BASE+ord+60);
	uint m=rx[2]+(rx[3]<<16);
	uint n=0;
	while(p<e)
		n+=*p++;
	return m==n;
}

static void jump(void){
	static void* tmp=(void*)0x08008000;
	usb_off();
	display_off();
	flash_begin();
	flash_erasePage(tmp);
	flash_copy(tmp,bootVect,1024);
	NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x8000);

	flash_erasePage(bootVect);
	flash_copy(bootVect,(void*)version_bottom,1024);
	flash_end();
	while(1)
		;	//	reboot;
}
/*static void reboot(void){
	usb_off();
	while(1)
		;
}*/
static void writeFirm(ushort idx,uint *code){
	flash_begin();
	ubyte *p=(ubyte*)((uint)idx+ROM_BASE);
	if(((idx+60)&0x3ff)<=60)
		flash_erasePage(p+60);
	flash_copy(p,code,60);
	flash_end();
}

//------------------------------------------------------------------------------

/*void checkVecTable(void){
	if(*(bootVect+1)==*(vecTable+1))
		return;
	flash_begin();
	flash_erasePage(bootVect);
	flash_copy(bootVect,vecTable,1024);
	flash_end();
	reboot();
}*/
void iap(ushort *rx){
	static ubyte status;
	#define	UPG_READY	0
	#define	UPG_DOING	1
	#define	UPG_ERROR	2
	if(rx){
		static ushort orp;
		ushort ord=rx[1];
		ushort ordBase=version_bottom;
		if(ord==0xffff){
			if(checkSum(rx,orp))
				jump();					// never return
			status=UPG_ERROR;
			return;
		}
		switch(status){
		case UPG_READY:
			if(ord!=ordBase)
				goto err;
			status=UPG_DOING;
			break;
		case UPG_DOING:
			if(ord==(orp+60))
				break;
			else if(ord==orp)
				goto ack;
err:		status=UPG_READY;
			rx[1]=ordBase;
			goto ack;
		default:						// UPG_ERROR
			return;
		}
		writeFirm(ord,(uint*)(rx+2));
		orp=ord;						// save previous idx
ack:	sendReport(rx);
	}else status=UPG_READY;				// reset
}